
#include <xbi/xbixex.h>

static char * _XBIXboxDefaultExecutableMagic = "XEX2";
static size_t _XBIXboxDefaultExecutableMagicLength = 4;

struct _XBIXboxEXecutable {
    byte_t * data;
    int size;
    
    int32_t moduleFlags;
    int32_t dataOffset;
    int32_t reserved;
    int32_t fileHeaderOffset;
    int32_t optionalHeaderEntryCount;    
    
    byte_t  mediaID[_XBIMediaIDLength];
};

XBIXboxEXecutable * XBIXEXCreateFromBuffer(byte_t * buffer, int size, XBIError * error) {
    XBIXboxEXecutable * newXEX = NULL;
    
    newXEX = malloc(sizeof(XBIXboxEXecutable));    
    if (newXEX == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    memset(newXEX, 0, sizeof(XBIXboxEXecutable));
    
    newXEX->data = malloc(size);
    if (newXEX->data == NULL) {
        *error = kXBINotEnoughMemory;
        goto error;
    }
    memcpy(newXEX->data, buffer, size);
    
    newXEX->size = size;
    
    XBILog("Checking for presence of 'XEX2' string in executable\n");
    // checking for xbox works for 360 dmis, but not xbox 1
    if (memcmp(newXEX->data, _XBIXboxDefaultExecutableMagic, _XBIXboxDefaultExecutableMagicLength) == 0) {
        XBILog("XEX magic cookie found\n");
    } else {
        XBILog("XEX magic cookie NOT found !\n");
        goto error;
    }
    
    XBIAssert(size > 24, "Xbox executable cannot be smaller than 24 bytes");
    
    newXEX->moduleFlags =              XBIReadBigEndianInt32AtAddress(newXEX->data + 0x4);
    newXEX->dataOffset =               XBIReadBigEndianInt32AtAddress(newXEX->data + 0x8);
    newXEX->reserved =                 XBIReadBigEndianInt32AtAddress(newXEX->data + 0xC);
    newXEX->fileHeaderOffset =         XBIReadBigEndianInt32AtAddress(newXEX->data + 0x10);
    newXEX->optionalHeaderEntryCount = XBIReadBigEndianInt32AtAddress(newXEX->data + 0x14);
    
    
    // Media ID
    memcpy(newXEX->mediaID, newXEX->data + newXEX->fileHeaderOffset + 0x140, _XBIMediaIDLength);
    XBILog("MediaID : %s\n", newXEX->mediaID);
    
    return newXEX;
    
error:
    XBIXEXDelete(newXEX);
    return NULL;
}

void XBIXEXDelete(XBIXboxEXecutable * xex) {
    if (xex != NULL) {
        if (xex->data != NULL) {
            free(xex->data);
            xex->data = NULL;
        }        
        free(xex);
    }
}

uint32_t XBIXEXGetCRC32(XBIXboxEXecutable * xex) {
    if (xex == NULL || xex->data == NULL || xex->size == 0)
        return 0;
    return XBIComputeCRC32(xex->data, xex->size);
}

#if 0
#pragma mark Accessors
#endif

byte_t * XBIXEXGetMediaID(XBIXboxEXecutable * xex) {
    if (xex != NULL)
        return xex->mediaID;
    return NULL;
}


void XBIXEXWriteToFile(XBIXboxEXecutable * xex, char * path) {
    if (xex != NULL) {
        FILE * file = fopen(path, "w");
        fwrite(xex->data, xex->size, 1, file);
        fclose(file);
    }
}